package com.goldman.base.common.sys.filter;

import com.goldman.http.common.s2s.model.WebThreadContext;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * 请求数据及响应数据日志
 */
@Component
@Slf4j
public class AccessLogFilter extends OncePerRequestFilter implements Ordered {

    private int order = Ordered.LOWEST_PRECEDENCE - 8;
    private String REQUEST_HEADER = "X-Request-Id";
    private List<String> FILTER_PATHS = Lists.newArrayList("/actuator/**");
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public int getOrder() {
        return order;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        try {
            //过滤上传文件请求
            if (request.getContentType() != null && request.getContentType().indexOf("multipart/form-data") == -1) {
                long startTime = System.currentTimeMillis();
                AccessRequestWrapper wrapperRequest = new AccessRequestWrapper(request);
                String requestId = request.getHeader(REQUEST_HEADER);
                WebThreadContext.setRequestId(requestId);
                ContentCachingResponseWrapper wrapperResponse = new ContentCachingResponseWrapper(response);
                //process
                filterChain.doFilter(wrapperRequest, wrapperResponse);

                if (!filterPath(request.getServletPath())) {
                    long costTime = System.currentTimeMillis() - startTime;
                    String respContent = extractResponse(wrapperResponse);
                    log.info("[IncomeHttp-{}] url={} httpCode={} executeTime={} requestId={} header={} requestBody={} responseRawContent={}",
                            request.getMethod(), extractRequestURL(request), response.getStatus(), costTime,
                            WebThreadContext.getRequestId(), extractRequestHead(request), extractRequest(wrapperRequest),
                            respContent);
                }
                //end
                wrapperResponse.copyBodyToResponse();
            } else {
                filterChain.doFilter(request, response);
            }
        } finally {
            WebThreadContext.clear();
        }
    }

    private boolean filterPath(String path) {
        for (String filterPath : FILTER_PATHS) {
            if (antPathMatcher.isPattern(filterPath)) {
                return antPathMatcher.match(filterPath, path);
            } else {
                return Objects.equals(filterPath, path);
            }
        }
        return false;
    }


    /**
     * 处理请求链接
     *
     * @param request
     * @return
     */
    private String extractRequestURL(HttpServletRequest request) {
        String queryString = request.getQueryString();
        if (StringUtils.isNotBlank(queryString)) {
            return request.getRequestURI() + "?" + request.getQueryString();
        } else {
            return request.getRequestURI();
        }
    }

    /**
     * 处理请求Head
     *
     * @param request
     * @return
     */
    private Map<String, String> extractRequestHead(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> headerMap = new HashMap<>(16);
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headerMap.put(headerName, headerValue);
        }
        return headerMap;
    }

    /**
     * 处理请求数据
     *
     * @param request
     */
    private String extractRequest(AccessRequestWrapper request) {
        if (null != request && !request.getRequestURI().contains("swagger")) {
            try {
                String body = IOUtils.toString(request.getInputStream());
                return body;
            } catch (IOException e) {
                log.error("处理请求日志失败: ", e);
            }
        }
        return "";
    }

    /**
     * 处理响应数据
     *
     * @param response
     */
    private String extractResponse(ContentCachingResponseWrapper response) {
        if (null != response) {
            String contentType = response.getContentType();
            //过滤非json格式的响应
            if (StringUtils.isEmpty(contentType) || contentType.contains("application/json") || contentType.contains("application/xml") || contentType.contains("text/xml")) {
                String body = byte2String(response.getContentAsByteArray(), response.getCharacterEncoding());
                return body;
            }
        }
        return "";
    }

    private String byte2String(byte[] content, String encoding) {
        String payload = StringUtils.EMPTY;
        if (null != content && content.length > 0) {
            try {
                payload = new String(content, encoding);
            } catch (UnsupportedEncodingException e) {
                payload = "[unknown]";
            }
        }
        return payload;
    }

}